package com.xiaoxin.experience.system.config;

import com.xiaoxin.experience.system.context.ApplicationContextHolder;
import com.xiaoxin.experience.util.StringUtil;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.http.client.SimpleClientHttpRequestFactory;

import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

/**
 * @author xiaoxin
 */
public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory
{
    private String clientCertPath;

    private String clientCertPassword;

    private String serverCertPath;

    private String serverCertPassword;

    private String trustServer;

    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod)
    {
        try
        {
            if (!(connection instanceof HttpsURLConnection))
            {
                //http协议
                super.prepareConnection(connection, httpMethod);
            }
            if ((connection instanceof HttpsURLConnection))
            {
                HttpsURLConnection httpsURLConnection = (HttpsURLConnection) connection;
                init();
                URL url = httpsURLConnection.getURL();
                String host = url.getHost();
                int port = url.getPort();
                httpsURLConnection.setSSLSocketFactory(getsSocketFactory(host + ":" + port));
                super.prepareConnection(httpsURLConnection, httpMethod);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private void init()
    {
        clientCertPath = ApplicationContextHolder.getProperty("ssl.cert.client.path");
        serverCertPath = ApplicationContextHolder.getProperty("ssl.cert.server.path");
        clientCertPassword = ApplicationContextHolder.getProperty("ssl.cert.client.password");
        serverCertPassword = ApplicationContextHolder.getProperty("ssl.cert.server.password");
        trustServer = ApplicationContextHolder.getProperty("ssl.cert.trust.server");
    }

    private boolean isValidCert()
    {
        if (StringUtil.isBlank(clientCertPath) || StringUtil.isBlank(serverCertPath) || StringUtil.isBlank(clientCertPassword) || StringUtil.isBlank(serverCertPassword))
        {
            return false;
        }
        if (!new File(clientCertPath).exists() || !new File(serverCertPath).exists())
        {
            return false;
        }
        return (clientCertPath.endsWith(".p12") || clientCertPath.endsWith(".jks")) && (serverCertPath.endsWith(".p12") || serverCertPath.endsWith(".jks"));
    }

    public SSLSocketFactory getsSocketFactory(String server) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException
    {
        if (!trustServer.equals(server) || !isValidCert())
        {
            return new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build().getSocketFactory();
        }
        try
        {
            // 初始化密钥库
            KeyManagerFactory keyManagerFactory = KeyManagerFactory
                    .getInstance("SunX509");
            KeyStore keyStore = getKeyStore(clientCertPath, clientCertPassword);
            keyManagerFactory.init(keyStore, clientCertPassword.toCharArray());

            // 初始化信任库
            TrustManagerFactory trustManagerFactory = TrustManagerFactory
                    .getInstance("SunX509");
            KeyStore trustkeyStore = getKeyStore(serverCertPath, serverCertPassword);
            trustManagerFactory.init(trustkeyStore);

            // 初始化SSL上下文
            SSLContext sslcontext = SSLContext.getInstance("TLS");
            sslcontext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory
                    .getTrustManagers(), null);
            return sslcontext.getSocketFactory();
        }
        catch (Exception e)
        {
            return new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build().getSocketFactory();
        }

    }

    private KeyStore getKeyStore(String path) throws Exception
    {
        return path.endsWith(".p12") ? KeyStore.getInstance("PKCS12") : KeyStore.getInstance("JKS");
    }

    private KeyStore getKeyStore(String keyStorePath, String password)
            throws Exception
    {
        try(FileInputStream is = new FileInputStream(keyStorePath))
        {
            KeyStore ks = getKeyStore(keyStorePath);
            ks.load(is, password.toCharArray());
            return ks;
        }
    }
}
